﻿using System.Reflection;
using System.Text.Json.Serialization;
using Devonline.Core;

namespace Devonline.AspNetCore;

/// <summary>
/// 查询过滤选项, 代表字段内的字段和值的关系
/// 此对象只处理最终形态的简单表达式, 不处理嵌套的形式. 典型样式为: 
/// Name eq '张三'
/// Age gt 20
/// Name co '张三'
/// not support: not logic
/// not support: contains(Name, '张三')
/// not support: contains(tolower(Name), 'lucy')
/// </summary>
public class FilterOption
{
    /// <summary>
    /// 当前简单模式的过滤表达式
    /// </summary>
    protected readonly string? _filter;
    /// <summary>
    /// 当前表达式的类型主体
    /// </summary>
    protected readonly Type? _type;

    /// <summary>
    /// 默认构造函数
    /// </summary>
    public FilterOption(Type type)
    {
        _type = type;
    }
    /// <summary>
    /// 接受字符串表达式 filter 参数的构造函数
    /// </summary>
    /// <param name="filter"></param>
    public FilterOption(Type type, string filter)
    {
        _type = type;
        _filter = filter;
        string[] values;
        if (_filter.StartsWith("(") && filter.EndsWith(")"))
        {
            _filter = _filter[1..^1];
        }

        //如果是 contains/startswith/endswith
        if (_filter.StartsWith(OperatorType.Contains.ToString().ToLowerInvariant()) || _filter.StartsWith(OperatorType.StartsWith.ToString().ToLowerInvariant()) || _filter.StartsWith(OperatorType.EndsWith.ToString().ToLowerInvariant()))
        {
            values = _filter.Split(new char[] { ',', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);
            if (values.Length != 3)
            {
                throw new ArgumentException($"filter {filter} expression error!");
            }

            Field = values[1];
            Operator = values[0].GetEnumValueByJsonName<OperatorType>();
        }
        else
        {
            values = _filter.Split(AppSettings.CHAR_SPACE, StringSplitOptions.RemoveEmptyEntries);
            if (values.Length != 3)
            {
                throw new ArgumentException($"filter {filter} expression error!");
            }

            Field = values[0];
            Operator = values[1].GetEnumValueByJsonName<OperatorType>();
        }

        var member = _type.GetMember(Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.IgnoreCase).FirstOrDefault();
        if (member == null || !(member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Field))
        {
            throw new ArgumentException($"the property or field {Field} of type {_type.Name} not exist!");
        }

        Value = values[2];
        FieldType = member.GetMemberType();
        if (FieldType.IsValueType)
        {
            FieldValue = Convert.ChangeType(Value, FieldType);
        }
        else if (FieldType == typeof(string) && Value.StartsWith(AppSettings.CHAR_QUOTE) && Value.EndsWith(AppSettings.CHAR_QUOTE))
        {
            Value = Value[1..^1];
            FieldValue = Value;
        }
        else
        {
            FieldValue = Value;
        }
    }

    /// <summary>
    /// 字段名
    /// </summary>
    [JsonPropertyName("field")]
    public string? Field { get; set; }
    /// <summary>
    /// 值
    /// </summary>
    [JsonPropertyName("value")]
    public string? Value { get; set; }
    /// <summary>
    /// 字段和值的关系, eq, ne, gt, gte, lt, lte...
    /// </summary>
    [JsonPropertyName("operator")]
    public OperatorType? Operator { get; set; }
    /// <summary>
    /// field type
    /// </summary>
    public Type? FieldType { get; private set; }
    /// <summary>
    /// value 对应的原始类型值
    /// </summary>
    public object? FieldValue { get; private set; }

    /// <summary>
    /// 重写的 ToString 方法输出字符串形式的表达式
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        if (string.IsNullOrWhiteSpace(Field))
        {
            return string.Empty;
        }

        return Operator switch
        {
            OperatorType.Contains or OperatorType.StartsWith or OperatorType.EndsWith => $"{typeof(OperatorType).GetJsonPropertyName((Operator ?? OperatorType.Equal).ToString())}({Field}, {GetFilterValue()})",
            _ => $"{Field} {typeof(OperatorType).GetJsonPropertyName((Operator ?? OperatorType.Equal).ToString())} {GetFilterValue()}"
        };
    }

    /// <summary>
    /// get expression from filter option
    /// </summary>
    /// <returns></returns>
    public string GetFilterExpression() => Operator switch
    {
        OperatorType.Equal => $"{Field} == {GetFilterValue()}",
        OperatorType.NotEqual => $"{Field} != {GetFilterValue()}",
        OperatorType.GreaterThan => $"{Field} > {FieldValue}",
        OperatorType.GreaterThanAndEqual => $"{Field} >= {FieldValue}",
        OperatorType.LessThan => $"{Field} < {FieldValue}",
        OperatorType.LessThanAndEqual => $"{Field} <= {FieldValue}",
        OperatorType.Contains => $"{Field}.Contains(\"{FieldValue}\")",
        OperatorType.StartsWith => $"{Field}.StartsWith(\"{FieldValue}\")",
        OperatorType.EndsWith => $"{Field}.EndsWith(\"{FieldValue}\")",
        _ => throw new ArgumentException($"{nameof(Field)} is not support the operation {Operator}"),
    };

    /// <summary>
    /// 拼接过滤条件
    /// </summary>
    /// <returns></returns>
    private object? GetFilterValue() => (FieldType != null && FieldType == typeof(string)) ? $"\"{FieldValue}\"" : FieldValue;
}

/// <summary>
/// 查询过滤选项, 代表字段内的字段和值的关系
/// 此对象只处理最终形态的简单表达式, 不处理嵌套的形式. 典型样式为: 
/// Name eq '张三'
/// Age gt 20
/// Name co '张三'
/// not support: not logic
/// not support: contains(Name, '张三')
/// not support: contains(tolower(Name), 'lucy')
/// </summary>
public class FilterOption<T> : FilterOption
{
    /// <summary>
    /// 默认构造函数
    /// </summary>
    public FilterOption() : base(typeof(T)) { }
    /// <summary>
    /// 接受字符串表达式 filter 参数的构造函数
    /// </summary>
    /// <param name="filter"></param>
    public FilterOption(string filter) : base(typeof(T), filter) { }
}